<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="epEtQ" id="epEtQ"><span data-lake-id="ua8899990" id="ua8899990">典型回答</span></h1>
  <p data-lake-id="u086dedab" id="u086dedab"><br></p>
  <p data-lake-id="u96ce10bc" id="u96ce10bc"><span data-lake-id="ucbc61f77" id="ucbc61f77">Java的堆内存分代是指将不同生命周期的堆内存对象存储在不同的堆内存区域中，这里的不同的堆内存区域被定义为“代”。这样做有助于提升垃圾回收的效率，因为这样的话就可以为不同的"代"设置不同的回收策略。</span></p>
  <p data-lake-id="u3ec5d67d" id="u3ec5d67d"><span data-lake-id="u5415a7d2" id="u5415a7d2">​</span><br></p>
  <p data-lake-id="u79be772b" id="u79be772b"><span data-lake-id="ud13c6d65" id="ud13c6d65">一般来说，</span><strong><span data-lake-id="ufa419360" id="ufa419360">Java中的大部分对象都是朝生夕死的</span></strong><span data-lake-id="u869016a5" id="u869016a5">，同时也有一部分对象会持久存在。因为如果把这两部分对象放到一起分析和回收，这样效率实在是太低了。</span><strong><span data-lake-id="u8640d0cf" id="u8640d0cf">通过将不同时期的对象存储在不同的内存池中，就可以节省宝贵的时间和空间，从而改善系统的性能。</span></strong></p>
  <p data-lake-id="u16daf39a" id="u16daf39a"><span data-lake-id="u3ea4ed47" id="u3ea4ed47">​</span><br></p>
  <p data-lake-id="u3198a3d0" id="u3198a3d0"><strong><span data-lake-id="u6585195a" id="u6585195a">Java的堆由新生代（Young Generation）和老年代（Old Generation）组成。</span></strong><span data-lake-id="uf6764c85" id="uf6764c85">新生代存放新分配的对象，老年代存放长期存在的对象。</span></p>
  <p data-lake-id="u19b6594b" id="u19b6594b"><span data-lake-id="uec27cb55" id="uec27cb55">​</span><br></p>
  <p data-lake-id="u5f8724a1" id="u5f8724a1"><span data-lake-id="u826811f5" id="u826811f5">新生代（Young）由年轻区（Eden）、Survivor区组成（</span><span data-lake-id="u89351dca" id="u89351dca" class="lake-fontsize-12" style="color: rgb(85, 85, 85)">From Survivor、To Survivor</span><span data-lake-id="u9ba77bf3" id="u9ba77bf3">）。默认情况下，新生代的Eden区和Survivor区的空间大小比例是8:2，可以通过-XX:SurvivorRatio参数调整。</span></p>
  <p data-lake-id="uced25c68" id="uced25c68"><span data-lake-id="ue5a317ba" id="ue5a317ba">​</span><br></p>
  <p data-lake-id="u9039e90f" id="u9039e90f"><img src="https://cdn.nlark.com/yuque/0/2022/png/5378072/1671680879420-22b483c1-b11c-483c-908b-5a8ef7ad7959.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_23%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="ub25c05b6" id="ub25c05b6"><span data-lake-id="ub3475cc5" id="ub3475cc5">​</span><br></p>
  <p data-lake-id="u15aa004c" id="u15aa004c"><span data-lake-id="ue0ac8070" id="ue0ac8070">很多对象都会出现在Eden区，当Eden区的内存容量用完的时候，GC会发起，非存活对象会被标记为死亡，存活的对象被移动到Survivor区。</span></p>
  <p data-lake-id="u9460e2d4" id="u9460e2d4"><br></p>
  <p data-lake-id="u86e9820a" id="u86e9820a"><span data-lake-id="udf395d7d" id="udf395d7d">如果Survivor的内存容量也用完，那么存活对象会被移动到老年代。</span></p>
  <p data-lake-id="uf30b5d0e" id="uf30b5d0e"><span data-lake-id="ua32ef0b3" id="ua32ef0b3">​</span><br></p>
  <p data-lake-id="u06ac5f17" id="u06ac5f17"><span data-lake-id="u00152bdb" id="u00152bdb">老年代（Old）是对象存活时间最长的部分，它由单一存活区（Tenured）组成，并且把经历过若干轮GC回收还存活下来的对象移动而来。在老年代中，大部分对象都是活了很久的，所以GC回收它们会很慢。</span></p>
  <p data-lake-id="u3a356266" id="u3a356266"><br></p>
  <h1 data-lake-id="RHhM4" id="RHhM4"><span data-lake-id="ua547148a" id="ua547148a">扩展知识</span></h1>
  <h2 data-lake-id="tmzq4" id="tmzq4"><span data-lake-id="u0f9291f4" id="u0f9291f4">对象的分代晋升</span></h2>
  <p data-lake-id="ud3725783" id="ud3725783"><br></p>
  <p data-lake-id="ufb4c9446" id="ufb4c9446"><span data-lake-id="uf84d6a1c" id="uf84d6a1c">一般情况下，对象将在新生代进行分配，首先会尝试在Eden区分配对象，当Eden内存耗尽，无法满足新的对象分配请求时，将触发新生代的GC(Young GC、MinorGC)，在新生代的GC过程中，没有被回收的对象会从Eden区被搬运到Survivor区，这个过程通常被称为"晋升"</span></p>
  <p data-lake-id="ub2cfa62a" id="ub2cfa62a"><span data-lake-id="u2bbec6ea" id="u2bbec6ea">​</span><br></p>
  <p data-lake-id="u07c6f3a2" id="u07c6f3a2"><span data-lake-id="u2ba949dd" id="u2ba949dd">同样的，</span><strong><span data-lake-id="u1a953b19" id="u1a953b19">对象也可能会晋升到老年代，触发条件主要看对象的大小和年龄</span></strong><span data-lake-id="u901f4777" id="u901f4777">。对象进入老年代的条件有三个，满足一个就会进入到老年代：</span></p>
  <ul list="u145eaf62">
   <li fid="ueb71ce0f" data-lake-id="uaa5004d6" id="uaa5004d6"><span data-lake-id="u52d9f485" id="u52d9f485">1、躲过15次GC。每次垃圾回收后，存活的对象的年龄就会加1，累计加到15次（jdk8默认的），也就是某个对象躲过了15次垃圾回收，那么JVM就认为这个是经常被使用的对象，就没必要再待在年轻代中了。具体的次数可以通过 -XX:MaxTenuringThreshold 来设置在躲过多少次垃圾收集后进去老年代。</span></li>
   <li fid="ueb71ce0f" data-lake-id="u3e360ec3" id="u3e360ec3"><span data-lake-id="u276c2ab7" id="u276c2ab7">2、动态对象年龄判断。规则：</span><strong><span data-lake-id="u4cf9ba64" id="u4cf9ba64">如果在Survivor空间中小于等于某个年龄的所有对象大小的总和大于Survivor空间的一半时，那么就把大于等于这个年龄的对象都晋升到老年代。</span></strong></li>
   <li fid="ueb71ce0f" data-lake-id="ub2a0b247" id="ub2a0b247"><span data-lake-id="u254ad2a0" id="u254ad2a0">3、大对象直接进入老年代。-XX:PretenureSizeThreshold 来设置大对象的临界值，大于该值的就被认为是大对象，就会直接进入老年代。（PretenureSizeThreshold默认是0，也就是说，默认情况下对象不会提前进入老年代，而是直接在新生代分配。然后就GC次数和基于动态年龄判断来进入老年代。）</span></li>
  </ul>
  <p data-lake-id="uc0624be8" id="uc0624be8"><span data-lake-id="u4c7e6f15" id="u4c7e6f15">针对上面的三点来逐一分析。</span></p>
  <p data-lake-id="u446b034b" id="u446b034b"><span data-lake-id="u3eb6484e" id="u3eb6484e">​</span><br></p>
  <h3 data-lake-id="RjKv0" id="RjKv0"><span data-lake-id="u00b04bce" id="u00b04bce">动态年龄判断</span></h3>
  <p data-lake-id="u5f551d45" id="u5f551d45"><span data-lake-id="uc1f3b4bc" id="uc1f3b4bc"> </span></p>
  <p data-lake-id="ud7480631" id="ud7480631"><span data-lake-id="ub4e97c9b" id="ub4e97c9b" class="lake-fontsize-12">为了能更好地适应不同程序的内存状况，HotSpot虚拟机并不是永远要求对象的年龄必须达到- XX:M axTenuringThreshold才能晋升老年代，他还有一个动态年龄判断的机制。</span></p>
  <p data-lake-id="u6c40a948" id="u6c40a948"><span data-lake-id="u9d6647d2" id="u9d6647d2" class="lake-fontsize-12">​</span><br></p>
  <p data-lake-id="u11c211f0" id="u11c211f0"><span data-lake-id="u37f8ddda" id="u37f8ddda" class="lake-fontsize-12">在《深入理解Java虚拟机（第三版）》中是这么描述动态年龄判断的过程的：</span></p>
  <p data-lake-id="ue9448071" id="ue9448071"><span data-lake-id="u6a75a15b" id="u6a75a15b" class="lake-fontsize-12">​</span><br></p>
  <p data-lake-id="u544f03f1" id="u544f03f1"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1697110000643-5b845e48-a453-4f6f-ae57-f3249777fd8f.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_43%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u03d8ac92" id="u03d8ac92"><span data-lake-id="u0205ae11" id="u0205ae11"> </span></p>
  <p data-lake-id="uae23ad11" id="uae23ad11"><strong><span data-lake-id="uc6aea950" id="uc6aea950" style="color: #DF2A3F">但是，这段描述是不正确的</span></strong><span data-lake-id="uabb34d2d" id="uabb34d2d" style="color: #DF2A3F">！</span></p>
  <p data-lake-id="u8558a29e" id="u8558a29e"><span data-lake-id="ub1a00ed4" id="ub1a00ed4">​</span><br></p>
  <p data-lake-id="u38a514c7" id="u38a514c7"><span data-lake-id="u3fd01cc3" id="u3fd01cc3">JVM中，动态年龄判断的代码如下：</span></p>
  <p data-lake-id="u80459424" id="u80459424"><span data-lake-id="uee2e3aae" id="uee2e3aae">​</span><br></p>
  <pre lang="java"><code>
uint ageTable::compute_tenuring_threshold(size_t survivor_capacity) {
  
  size_t desired_survivor_size = (size_t)((((double) survivor_capacity)*TargetSurvivorRatio)/100);
  size_t total = 0;
  uint age = 1;
  while (age &lt; table_size) {
    total += sizes[age];
    if (total &gt; desired_survivor_size) break;
    age++;
  }
  uint result = age &lt; MaxTenuringThreshold ? age : MaxTenuringThreshold;
    ...
}
</code></pre>
  <p data-lake-id="u9d11b467" id="u9d11b467"><span data-lake-id="uec5f4c7a" id="uec5f4c7a"> </span></p>
  <p data-lake-id="ued2202e3" id="ued2202e3"><span data-lake-id="uc9182d5c" id="uc9182d5c">它的过程是从年龄小的对象开始，不断地累加对象的大小，当年龄达到N时，刚好达到</span><span data-lake-id="u46dfffd6" id="u46dfffd6" class="lake-fontsize-11" style="color: rgb(31, 35, 40)">TargetSurvivorRatio这个阈值，那么就把所有年龄大于等于N的对象全部晋升到老年代去！</span></p>
  <p data-lake-id="u3052e11e" id="u3052e11e"><span data-lake-id="u00f46101" id="u00f46101" class="lake-fontsize-11" style="color: rgb(31, 35, 40)">​</span><br></p>
  <p data-lake-id="uc1c694c4" id="uc1c694c4"><span data-lake-id="u74b13ef4" id="u74b13ef4" class="lake-fontsize-11" style="color: rgb(31, 35, 40)">所以，这过程应该是这样的：</span></p>
  <p data-lake-id="ua2614eb2" id="ua2614eb2"><span data-lake-id="uc64493a5" id="uc64493a5" class="lake-fontsize-11" style="color: rgb(31, 35, 40)">​</span><br></p>
  <p data-lake-id="ue36bf18f" id="ue36bf18f"><strong><span data-lake-id="ue481cfd0" id="ue481cfd0">如果在Survivor空间中小于等于某个年龄的所有对象大小的总和大于Survivor空间的一半时，那么就把大于等于这个年龄的对象都晋升到老年代。</span></strong></p>
  <p data-lake-id="u53f4f450" id="u53f4f450"><br></p>
  <h2 data-lake-id="hBJWe" id="hBJWe"><span data-lake-id="u15a58569" id="u15a58569">新生代如果只有两个区域可以吗？</span></h2>
  <p data-lake-id="ub6514ba9" id="ub6514ba9"><br></p>
  <p data-lake-id="uee5d778f" id="uee5d778f"><br></p>
  <h2 data-lake-id="pM97w" id="pM97w"><span data-lake-id="u71abc343" id="u71abc343">什么是永久代？</span></h2>
  <p data-lake-id="u5274313c" id="u5274313c"><br></p>
  <p data-lake-id="u7de53084" id="u7de53084"><span data-lake-id="u0c91bed1" id="u0c91bed1">永久代（Permanent Generation）是HotSpot虚拟机在以前版本中使用的一个永久内存区域，是JVM中垃圾收集堆之外的另一个内存区域，它主要用来实现方法区的，其中存储了Class类信息、常量池以及静态变量等数据。</span></p>
  <p data-lake-id="u13d12cec" id="u13d12cec"><span data-lake-id="u8bc98dfb" id="u8bc98dfb">​</span><br></p>
  <p data-lake-id="u2cd99d26" id="u2cd99d26"><span data-lake-id="u442acd7d" id="u442acd7d">Java 8以后，永久代被重构为元空间（MetaSpace）。</span></p>
  <p data-lake-id="u5cec488e" id="u5cec488e"><span data-lake-id="u1af411ef" id="u1af411ef">​</span><br></p>
  <p data-lake-id="uf7cac1ec" id="uf7cac1ec"><span data-lake-id="u44b071dc" id="u44b071dc">但是，和新生代、老年代一样，永久代也是可能会发生GC的。而且，永久代也是有可能导致内存溢出。只要永久代的内存分配超过限制指定的最大值，就会出现内存溢出。</span></p>
  <p data-lake-id="u3399b940" id="u3399b940"><br></p>
  <h2 data-lake-id="llj6N" id="llj6N"><span data-lake-id="ued52361a" id="ued52361a">不同分代的GC方式？</span></h2>
  <p data-lake-id="u3bfbc169" id="u3bfbc169"><br></p>
  <p data-lake-id="u371d6ae9" id="u371d6ae9"><span data-lake-id="u8a8b27e1" id="u8a8b27e1">Minor GC（YoungGC），主要用于对新生代垃圾回收。</span></p>
  <p data-lake-id="u0d80a594" id="u0d80a594"><span data-lake-id="ud498c9de" id="ud498c9de">Full GC，除了会对新生代垃圾回收以外，还会对老年代或者永久代进行回收。相比较于Minor GC，Full GC的收集频率更低，耗时更长。</span></p>
  <p data-lake-id="ufdfa83b6" id="ufdfa83b6"><br></p>
  <p data-lake-id="u50f9aa2b" id="u50f9aa2b"><br></p>
 </body>
</html>